/*****************************************************************************
 *    Dans le cadre d'un processus de creation d'un mecanisme d'inference    *
 * par chainage avant ou arriere, ici sera defini la procedure de chainage   *
 * arriere.                                                                  *
 *    Les procedures communes et les utilitaires se trouvent dans le fichier *
 * KB.java.                                			                         *
 *                                                                           *
 *****************************************************************************/

/* Importations */
package ChainArriere;
import java.io.*;			// Pour lire sur la console
import java.util.*;			// Pour les Vector


/* Classe realisant le chainage arriere */

public class backward {

	private static Vector regles = new Vector();
	private static Vector posables = new Vector();
	
	
	private static Vector listConclusion = new Vector();
	private static Vector listPremices = new Vector();
	private static Vector listVisite = new Vector();
	private static KB kb;
	
	public static boolean arrete =  false;
	public static String ChaineResul = null;
	public static String ChaineQuestionPartiente = null;
	
	static public boolean chainage(Vector regles, Question question) {

		if (question.resultat() != null) 
			return true;	// Peut deja conclure

		Boolean solution;				// A-t-on trouve une solution?
		boolean changement, avance;		// Note si on progresse ou pas
		Vector tmp;

		// On initialise la frange
		Vector frange = new Vector();
		Vector lesfaits = new Vector();

		
		// Prendre la valeur de gestion
		Vector laQ = question.getFaits();
		for (int i = 0, max = laQ.size(); i < max; i++)
			lesfaits.add(laQ.get(i));

		// Tant qu'il y a des regles qu'on peut ajouter
		avance = true;
		while (avance) 
		{
			avance = false;

			// On verifie si les regles qu'on a en frange peuvent etre valides
			changement = true;
			while (changement) 
			{		// ... tant qu'il y a du changement
				changement = false;
				for (int i = 0, max = frange.size(); i < max; i++)
					if (((Regle) frange.get(i)).go()) 
					{		// Une regle infere!
						changement = true;
					}
			}

			// On verifie si on a une solution
			solution = question.resultat();		// Une solution?
			if (solution != null) 
			{				// On peut conclure!
				afficher(question);
				return true;
			}

			// On ajoute une (nouvelle) regle liee a un fait de la frange
			for (int i = 0, max = lesfaits.size(); i < max; i++)
			{
				for (int j = 0, top = regles.size(); j < top; j++)
				{
					if (lesfaits.get(i) == ((Regle) regles.get(j)).getConclusion())
					{						
						if (pasDejaLaDansListe(regles.get(j), frange))
						{
							frange.add(regles.get(j));
							avance = true;
							// On ajoute ici les faits a determiner
							tmp = ((Regle) regles.get(j)).getPremices();
							for (int k = 0, fin = tmp.size(); k < fin; k++)
								if(pasDejaLaDansListe(tmp.get(k), lesfaits))
								{
									lesfaits.add(tmp.get(k));										
								
								}
						}
					}	
			
				}			
			}

		}

		// Pas conclu, plus de changement ...
		return false;

	}

	// Petite fonction pour determiner si un objet est deja dans un vecteur
	static private boolean pasDejaLaDansListe(Object o, Vector v) {
		for (int i = 0, max = v.size(); i < max; i++)
			if (v.get(i) == o) 
				return false;
		return true;
	}

	// Obetenir la reponse de l'utilisateur
	static public void setQuestionPertiente(String question, boolean reponse)
	{
		boolean flag = true;
		for(int i = 0 ; i < posables.size() && flag; i++)
		{
			Fait t = (Fait)posables.elementAt(i);
			if (t.libelle.equals(question))
			{
				t.valeur = reponse;
				t.determination = true;
				
				flag = false;
			}
		}
	}
	
	// Pour afficher l'arbre de preuve
	static public void afficher(Question question) 
	{
		
		ChaineResul = "REPONSE: " + (question.resultat().booleanValue()?"VRAI":"FAUX")+"\n";
		Vector v = question.getFaits();
		Fait f;
		for (int i = 0; i< v.size(); i++)
		{
			f = (Fait) v.get(i);			
			ChaineResul += "PREUVE : " + f.libelle + "\n";			
			ChaineResul += preuve(f.getSource());
		}
		
		String []arr = ChaineResul.split("\n");
		
		Vector kq = new Vector();
		ChaineResul = "";
		for(int i = 0 ; i < arr.length ; i++)
		{
			if(!kq.contains(arr[i]))
			{
				kq.add((String)arr[i]);
				ChaineResul += arr[i]+"\n";
			}
		}
		
		
		
	}

	static public String preuve(Regle regle) 
	{
		String txt = "";

		if (regle == null)
			return "";					// Cas terminal ...

		// Sinon, pour chaque premice
		Vector premices = regle.getPremices();

		for (int i = 0; i < premices.size(); i++)
			txt += preuve(((Fait) premices.get(i)).getSource());	

		txt += "  De règle : " + regle + " ; on a  " + regle.getConclusion() + "\n";		
		
		return txt;
	}

	// La fonction principale
	public static String Run(String demande)
	{
		
		// Creation d'une representation de la question posee		
		Question question = new Question(demande);
		
		
		// Pour initialize la memoire de travail (BF)
		BaseDeFait(demande);

		posables = listVisite;	
		
		/* Tant qu'on n'a pas fini de demander des indices a l'utilisateur et qu'on n'a pas conclu, on retente le chainage ... */
		int numero = 0;		// Numero de la question a poser...
		while(!chainage(regles,question) && numero < posables.size())
		{
			// Pour poser une question a l'utilisateur				
			if(((Fait)posables.get(numero)).determination == false)
			{
				ChaineQuestionPartiente = ((Fait) posables.get(numero)).libelle;			
				return ChaineQuestionPartiente;
			}			
			numero ++;			

		}
		
		// Rendu ici on a epuise toutes les possibilites, on fini discretement.
		if (question.resultat() == null)
		{
			arrete = true;
			ChaineResul = "NON CONCLUANT";
			
			return ChaineResul;
		}
		
		return null;

	}
	
	// Lire des infos sur l'ecran
	public static void Lire(String path)
	{
		// On lit le fichier, creer la KB, ajoute les questions et les regles		
		kb = KB.lire(path);

		// Obtenir la liste des regles (un vecteur de Regle)
		regles = kb.rules();
	}
	
	// Donner les regles 
	public static Vector listRegles()
	{
		Vector resul = new Vector();		
		for (int i = 0; i< regles.size(); i++)
		{
			Regle temp = (Regle) regles.get(i);
			resul.add(temp.toString());
		}		
		return resul;
	}
	// Donner les questions proposees
	public static Vector listQuestionProposee()
	{
		Vector resul = new Vector();		
		
		for(int i = 0 ; i < regles.size(); i ++)
		{
			Fait temp = (Fait)((Regle)regles.elementAt(i)).getConclusion();
			if (!resul.contains(temp.libelle))
				resul.add((String)temp.libelle);
		}
		
		return resul;
	}
	
	
	private static void UpdateVisite(String demande)
	{
		for(int i = 0 ; i < regles.size(); i++)
		{			
			Regle rg = (Regle)regles.elementAt(i);	
			if (rg.getConclusion().libelle.equals(demande))
			{	
				// Mis a jour la conclusion 
				((Regle)regles.elementAt(i)).getConclusion().viste = true;
				
				
				// Mis a jour les premices 
				Vector list = new Vector();
				list = rg.getPremices();
				for (int j = 0 ; j < list.size(); j++)
				{
					Fait temp = (Fait)list.elementAt(j);	
					if(temp.libelle.equals(demande))
					{
						temp.viste = true;
					}
				}
			}
		}
	}
	
	// True : liste change 
	// False : ne rien 
	private static boolean ListFaitVisite(String demande)
	{
	
//		System.out.println("variable:" + demande);
		
		
		
		// Nous mettons tous les regles qui contient ce fait 
		UpdateVisite(demande);
		
		
		boolean resul = false;
		for(int i = 0 ; i < regles.size(); i++)
		{
			
			Regle rg = (Regle)regles.elementAt(i);		
			
			if (rg.getConclusion().libelle.equals(demande))
			{		
										
				if (listVisite.contains(rg.getConclusion()))
				{
					listVisite.remove(rg.getConclusion());
					resul = true;
					
					// Indique que le fait a deja visite
					rg.getConclusion().viste = true;
				}
				
				Vector list = new Vector();
				list = rg.getPremices();			
				
				for (int j = 0 ; j < list.size(); j++)
				{
					Fait temp = (Fait)list.elementAt(j);				
					
					if (!temp.viste)
					{
						temp.viste = true;
						listVisite.add((Fait)temp);		
						resul = true;			
					}
					
					
				}
				
			}
			
		}
		
		return resul;
		
	}
	
	// Donner les memoires de travail
	private static void BaseDeFait(String demande)
	{
			
		// Trouver tous les premices et tous les conclusions
		for(int i = 0 ; i < regles.size(); i++)
		{
			
			Regle rg = (Regle)regles.elementAt(i);			
			
			if (!listConclusion.contains((Fait)rg.getConclusion()))
			{
				listConclusion.add((Fait)rg.getConclusion());
			}	
					
			Vector temp = new Vector();
			temp = rg.getPremices();
			for(int j = 0 ; j < temp.size(); j++)
			{
				if(!listPremices.contains((Fait)temp.elementAt(j)))
				{
					listPremices.add((Fait)temp.elementAt(j));
				}
			}
										
		}
		
	
		ListFaitVisite(demande);		
//		DisplayListVisite();		
	
		int numero = 0;		
		while(!verifierArrete() && numero < listVisite.size())
		{
			Fait t = (Fait)listVisite.elementAt(numero);
			
			// True : liste change 
			// False : ne rien 
			boolean change= ListFaitVisite(t.libelle);
			
//			DisplayListVisite();
			
			if(!change)				
				numero ++;
			
//			System.out.println("size:" + listVisite.size());
		}		
		
		Fait fin = (Fait)listVisite.lastElement();
		ListFaitVisite(fin.libelle);
//		DisplayListVisite();
		
		
		// Supprimer les faits visite appartient a la conclusion 
		for(int i = 0 ; i < listVisite.size(); i++)
		{
			Fait t = (Fait)listVisite.elementAt(i);
			if(listConclusion.contains(t))
				listVisite.remove(t);
		}
		
//		DisplayListVisite();		
	}	
	
	
//	public static void DisplayListVisite()
//	{
//		for(int i = 0 ; i < listVisite.size(); i++)
//		{
//			Fait t = (Fait)listVisite.elementAt(i);
//			System.out.print(t.libelle+",");
//		}
//		System.out.println("\n");
//	}
	
	
	public static boolean verifierArrete()
	{
		boolean resul = true;
//		for(int i = 0 ; i < listPremices.size();i++)
//		{
//			Fait temp = (Fait)listPremices.elementAt(i);	
//			System.out.print("variable:"+temp.libelle + ";visite:" + temp.viste + "\n");
//		}
//		System.out.print("\n\n");
		
		
		for(int i = 0 ; i < listPremices.size();i++)
		{
			Fait temp = (Fait)listPremices.elementAt(i);			
			if(!temp.viste)
			{
				return false;
			}
		}
		
		return resul;
	}
	
	
	public static Vector listBaseDeFait()
	{
		Vector resul = new Vector();
		for(int i = 0 ; i < listPremices.size();i++)
		{
			Fait temp = (Fait)listPremices.elementAt(i);
			if(temp.determination && !resul.contains((String)temp.libelle))
			{
				if (temp.valeur)
					resul.add(temp.libelle+",VRAI");
				else
					resul.add(temp.libelle+",FAUX");
			}
		}
		
		return resul;
	}
	// Donner les questions Pertientes
	public static Vector listQuestionPertientes()
	{
		Vector resul = new Vector();
		for (int i = 0; i< posables.size(); i++)
		{
			Fait temp = (Fait) posables.get(i);
			resul.add(temp.libelle);
		}
		return resul;
	}
	
	
}
